// 标签 tag 正则，变成正则后， 得到 aa-aa 或 a
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`;

//可以得到命名空间标签 <div:aa>    ==> "((?:[a-zA-Z_][\-\.0-9_a-zA-Z]*\:)?[a-zA-Z_][\-\.0-9_a-zA-Z]*)"
const qnameCapture = `((?:${ncname}\\:)?${ncname})`;

// 标签开头的正则 捕获的内容是标签名  ==>  /^<((?:[a-zA-Z_][\-\.0-9_a-zA-Z]*\:)?[a-zA-Z_][\-\.0-9_a-zA-Z]*)/
const startTagOpen = new RegExp(`^<${qnameCapture}`);

// 匹配标签结尾的 </div> ==>  /^<\/((?:[a-zA-Z_][\-\.0-9_a-zA-Z]*\:)?[a-zA-Z_][\-\.0-9_a-zA-Z]*)[^>]*>/
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`);

// 匹配属性attr的，分组对应三种方式 a="1", a=1， a='1'的某一种,第一个分组匹配 a ，最后一个大分组内的三个分组匹配值
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/;

const startTagClose = /^\s*(\/?)>/; // 匹配标签结束的 >
// const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g; // 匹配小胡子语法 {{xxx}}
/*
 * 需要将 template（模板）解析成一个 ast 语法树对象
 * 以下就是 ast 示例，将 dom 结构用对象表示
 * */

// let ast = {
//   tag: "div",
//   type: 1, // 1是元素， 3是文本
//   parent: undefined,
//   arrtsList: [{ name: "id", value: "#app" }],
//   arrtsMap: [{ name: "id", calss: "demo" }],
//   children: [{ type: 3, text: "我是vue" }],
// };

// 解析html ，得到 ast 语法树
export function parseHTML(html) {
  // ast 单元的而基本结构
  function createASTElement(tag, attrs) {
    return {
      tag,
      type: 1,
      children: [],
      attrs,
      parent: null,
    };
  }

  let root = null;
  let currentParent = null; // 当前节点的父级，作为指针使用
  let stack = [];

  // 遇到开始标签
  function start(tagName, attrs) {
    let element = createASTElement(tagName, attrs);
    if (!root) {
      root = element;
    }
    currentParent = element;
    stack.push(element);
  }

  // 遇到结束标签
  function end(tagName) {
    // 当解析到结束标签时，就表示得到了一个完整的标签，需要将指针回退 1位 , 同时
    // 将此完整的标签提取出来塞进当前指针标签中
    let element = stack.pop();
    currentParent = stack[stack.length - 1];
    if (currentParent) {
      element.parent = currentParent;
      currentParent.children.push(element);
    }
  }

  // 处理文本标签
  function chars(text) {
    text = text.replace(/\s/g, ""); // 将空格删除替换
    if (text) {
      currentParent.children.push({
        type: 3, // 文本节点
        text,
      });
    }
  }

  // 已经匹配到的就删掉
  function advance(n) {
    html = html.substring(n); // 从 n 起，截取到最后
  }

  function parseStartTag() {
    // log("startTagOpen-----", startTagOpen);
    const start = html.match(startTagOpen);
    // /^<((?:[a-zA-Z_][\-\.0-9_a-zA-Z]*\:)?[a-zA-Z_][\-\.0-9_a-zA-Z]*)/ 分组,不使用全局匹配g
    // 第一个元素是以贪婪模式找到的最长的匹配，之后的元素依次为该匹配中的第一、第二、第三 ......个分组，
    // 这里只有1个分组，所以也就只匹配到1个分组结果，也就是”div"。
    if (start) {
      // start 为 ["<div", "div", index: 0, input: 整个html, groups: undefined]
      let match = {
        tagName: start[1], // 标签名 div
        attrs: [], // 标签内的属性
      };
      // log("开始标签---", start[1]);
      advance(start[0].length);

      let end, attr;
      // 此 while 循环只解析开始标签里的属性  <div id='xxx' a='323' b='987'>
      // 没有找到右 > 就一直解析，并收集属性,当 while 结束时就到了 >
      while (
        !(end = html.match(startTagClose)) &&
        (attr = html.match(attribute))
      ) {
        // log("attr-------", attr);
        // [' id="app"', "id", "=", "app", undefined, undefined, index: 0, input: html的当前值, groups: undefined]
        advance(attr[0].length);
        match.attrs.push({
          name: attr[1],
          value: attr[3] || attr[4] || attr[5] || true,
        });
      }
      // 匹配到开始标签的结尾 > 了,属性匹配完毕 < div >
      if (end) {
        // log("开始标签的结尾-----", end[0]);
        advance(end[0].length);
        return match;
      }
    }
  }

  // log("html--起始--", html);
  while (html) {
    let text;
    let textEnd = html.indexOf("<"); // 开始标签的头或闭合标签的头 <
    // 收集开始标签内的属性
    if (textEnd === 0) {
      // parseStartTag 执行一次就收集完一个开始标签
      let startTagMatch = parseStartTag(); // 匹配分析开始标签内的属性并收集，得到 {tagName:''div,attrs:{xxx}}

      // 匹配到了开始标签的 > 符号,就开始下一次循环，可能就进入 text 了
      if (startTagMatch) {
        // log("html--00--", html);
        start(startTagMatch.tagName, startTagMatch.attrs);
        continue;
      }
      // log("html--11--", html);

      let endTagMatch = html.match(endTag);
      //  ["</div>", "div", index: 0, input: xxx, groups: undefined]
      if (endTagMatch) {
        // log("结束标签---", endTagMatch[1]);
        advance(endTagMatch[0].length);
        end(endTagMatch[1]);
        continue;
      }
      // log("html--22--", html);
    }

    // 收集开始标签到下一个开始标签之间的内容，可能为空，或者为文本
    if (textEnd > 0) {
      text = html.substring(0, textEnd);
    }
    if (text) {
      advance(text.length);
      chars(text);
      // log("文本---", text);
    }
  }
  return root;
}
